package indigo.mybatis.entity;

import static java.util.Objects.requireNonNull;

import java.io.Serializable;
import java.util.Collections;
import java.util.Map;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;

public class EntityMetadata<T> implements Serializable {
	private static final long serialVersionUID = -9069997617975966055L;

	private final Class<T> type;
	private final String name;
	private final String tableName;
	private final String idPropertyName;
	private final Map<String, PropertyMetadata> properties;
	private final String cacheNamespaceRef;
	private final CacheMetadata cache;

	protected EntityMetadata(Builder<T> builder) {
		this.type = builder.getType();
		this.name = builder.getName();
		this.tableName = builder.getTableName();
		this.idPropertyName = builder.getIdPropertyName();
		this.properties = builder.getProperties();
		this.cacheNamespaceRef = builder.getCacheNamespaceRef();
		this.cache = builder.getCache();
	}

	public Class<T> getType() {
		return type;
	}

	public String getName() {
		return name;
	}

	public String getTableName() {
		return tableName;
	}

	public String getCacheNamespaceRef() {
		return cacheNamespaceRef;
	}

	public CacheMetadata getCache() {
		return cache;
	}

	public String getIdPropertyName() {
		return idPropertyName;
	}

	public Map<String, PropertyMetadata> getProperties() {
		return properties;
	}

	public static class Builder<T> {
		private final Class<T> type;
		private String name;
		private String tableName;
		private String idPropertyName;
		private Map<String, PropertyMetadata> properties = Collections.emptyMap();
		private String cacheNamespaceRef;
		private CacheMetadata cache;

		public Builder(Class<T> type) {
			this.type = requireNonNull(type, "type cannot be null");
		}

		public EntityMetadata<T> build() {
			requireNonNull(name, "name cannot be null");
			requireNonNull(tableName, "tableName cannot be null");
			requireNonNull(idPropertyName, "idPropertyName cannot be null");
			if (cacheNamespaceRef != null && cache != null) {
				throw new IllegalArgumentException("cacheNamespaceRef and cache cannot be set together");
			}
			return new EntityMetadata<T>(this);
		}

		public void setName(String name) {
			this.name = Strings.emptyToNull(name);
		}

		public void setTableName(String tableName) {
			this.tableName = Strings.emptyToNull(tableName);
		}

		public void setIdPropertyName(String idPropertyName) {
			this.idPropertyName = Strings.emptyToNull(idPropertyName);
		}

		public void setProperties(Map<String, PropertyMetadata> properties) {
			this.properties = properties != null ? ImmutableMap.copyOf(properties) : Collections.emptyMap();
		}

		public void setCacheNamespaceRef(String cacheNamespaceRef) {
			this.cacheNamespaceRef = Strings.emptyToNull(cacheNamespaceRef);
		}

		public void setCache(CacheMetadata cache) {
			this.cache = cache;
		}

		public Class<T> getType() {
			return type;
		}

		public String getName() {
			return name;
		}

		public String getTableName() {
			return tableName;
		}

		public String getIdPropertyName() {
			return idPropertyName;
		}

		public Map<String, PropertyMetadata> getProperties() {
			return properties;
		}

		public String getCacheNamespaceRef() {
			return cacheNamespaceRef;
		}

		public CacheMetadata getCache() {
			return cache;
		}
	}
}
